package com.easylive.component;

import cn.hutool.core.bean.BeanUtil;

import com.easylive.entity.config.AppConfig;
import com.easylive.entity.dto.VideoInfoEsDTO;
import com.easylive.entity.enums.EsCountFieldEnum;
import com.easylive.entity.po.UserInfo;
import com.easylive.entity.po.VideoInfo;
import com.easylive.entity.query.SimplePage;
import com.easylive.entity.query.UserInfoQuery;
import com.easylive.entity.vo.PaginationResultVO;
import com.easylive.exception.BusinessException;
import com.easylive.mappers.UserInfoMapper;
import com.easylive.utils.JsonUtils;
import com.easylive.utils.StringTools;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import sun.awt.AppContext;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
@Slf4j
public class EsSearchComponent {

    @Resource
    private AppConfig config;
    @Resource
    private RestHighLevelClient client;
    @Resource
    private UserInfoMapper userInfoMapper;

    private Boolean isExistIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest(config.getEsIndexVideoName());
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }
    public void createIndex(){
        try {
            if(isExistIndex()){
                return;
            }
            CreateIndexRequest request = new CreateIndexRequest(config.getEsIndexVideoName());
            request.settings("{\"analysis\": {\n" +
                    "       \"analyzer\": {\n" +
                    "         \"comma\": {\n" +
                    "           \"type\": \"pattern\",\n" +
                    "           \"pattern\": \",\"\n" +
                    "         }\n" +
                    "       }\n" +
                    "     }}", XContentType.JSON);
            request.mapping("{\n" +
                    "    \"properties\": {\n" +
                    "        \"videoId\": {\n" +
                    "            \"type\": \"text\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"userId\": {\n" +
                    "            \"type\": \"text\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"videoCover\": {\n" +
                    "            \"type\": \"text\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"videoName\": {\n" +
                    "            \"type\": \"text\",\n" +
                    "            \"analyzer\": \"ik_max_word\"\n" +
                    "        },\n" +
                    "        \"tags\": {\n" +
                    "            \"type\": \"text\",\n" +
                    "            \"analyzer\": \"comma\"\n" +
                    "        },\n" +
                    "        \"playCount\": {\n" +
                    "            \"type\": \"integer\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"danmuCount\": {\n" +
                    "            \"type\": \"integer\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"collectCount\": {\n" +
                    "            \"type\": \"integer\",\n" +
                    "            \"index\": false\n" +
                    "        },\n" +
                    "        \"createTime\": {\n" +
                    "            \"type\": \"date\",\n" +
                    "            \"format\": \"yyyy-MM-dd HH:mm:ss\",\n" +
                    "            \"index\": false\n" +
                    "        }\n" +
                    "    }\n" +
                    "}",XContentType.JSON);
            CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
            boolean acknowledged = response.isAcknowledged();
            if(!acknowledged){
                throw new BusinessException("初始化es失败");
            }
        }catch (IOException e){
            log.error("初始化es失败！",e);
            throw new BusinessException("初始化es失败");

        }
    }

    public void saveDoc(VideoInfo info) {
        try {
            if(docExist(info.getVideoId())){
                updateDoc(info);
            }else {
                VideoInfoEsDTO videoInfoEsDTO = new VideoInfoEsDTO();
                BeanUtils.copyProperties(info,videoInfoEsDTO);
                videoInfoEsDTO.setCollectCount(0);
                videoInfoEsDTO.setDanmuCount(0);
                videoInfoEsDTO.setPlayCount(0);
                IndexRequest indexRequest =  new IndexRequest(config.getEsIndexVideoName());
                indexRequest.id(info.getVideoId()).source(JsonUtils.convertObj2Json(videoInfoEsDTO),XContentType.JSON);
                client.index(indexRequest, RequestOptions.DEFAULT);
            }
        }catch (IOException e){
            log.error("保存es文档失败！",e);
            throw new BusinessException("保存es文档失败！");
        }


    }
    private Boolean docExist(String id) throws IOException {
        GetRequest getRequest = new GetRequest(config.getEsIndexVideoName(),id);
        GetResponse response =  client.get(getRequest,RequestOptions.DEFAULT);
        return response.isExists();
    }

    public void updateDoc(VideoInfo videoInfo){
      try {
          videoInfo.setLastUpdateTime(null);
          videoInfo.setCreateTime(null);

          Map<String,Object> dataMap = new HashMap<>();
          Field[] fields = videoInfo.getClass().getDeclaredFields();
          for(Field field:fields){
              String methodName = "get" + StringTools.upperCaseFirstLetter(field.getName());
              Method method = videoInfo.getClass().getMethod(methodName);
              Object object = method.invoke(videoInfo);
              if(object != null && object instanceof String && !StringTools.isEmpty(object.toString()) || object != null && !(object instanceof String)){
                  dataMap.put(field.getName(),object);
              }
          }
          if(dataMap.isEmpty()){
              return;
          }
          UpdateRequest updateRequest = new UpdateRequest(config.getEsIndexVideoName(),videoInfo.getVideoId());
          updateRequest.doc(dataMap);
          client.update(updateRequest, RequestOptions.DEFAULT);
      }catch (Exception e){
            log.error("更新es文档失败！",e);
            throw new BusinessException("更新es文档失败！");

      }
    }


    public void updateDocCount(String videoId,String fieldName,Integer count) {
        try {
            UpdateRequest updateRequest = new UpdateRequest(config.getEsIndexVideoName(),videoId);
            Script script = new Script(ScriptType.INLINE,"painless","ctx._source."+fieldName+"+= params.count", Collections.singletonMap("count",count));
            updateRequest.script(script);
            client.update(updateRequest, RequestOptions.DEFAULT);


        }catch (IOException e){
            log.error("更新es数量失败！",e);
            throw new BusinessException("更新es数量失败！");
        }


    }

    public void delDoc(String videoId){
        try {
            DeleteRequest deleteRequest = new DeleteRequest(config.getEsIndexVideoName(),videoId);
            client.delete(deleteRequest, RequestOptions.DEFAULT);
        }catch (IOException e){
            log.error("删除es文档失败！",e);
            throw new BusinessException("删除es文档失败！");
        }
    }

    public PaginationResultVO<VideoInfo> search(Boolean highlight,String keyword,Integer pageNo,Integer pageSize,Integer orderType) throws IOException {
        EsCountFieldEnum esCountFieldEnum = EsCountFieldEnum.getEnumByType(orderType);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword,"videoName","tags"));
        if(highlight){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("videoName");
            highlightBuilder.preTags("<span class='highlight'>");
            highlightBuilder.postTags("</span>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        searchSourceBuilder.sort("_score", SortOrder.ASC);
        if(orderType != null){
            searchSourceBuilder.sort(esCountFieldEnum.getFieldName(),SortOrder.DESC);
        }

        pageNo = pageNo == null?1:pageNo;
        pageSize = pageSize == null?20:pageSize;
        searchSourceBuilder.from((pageNo-1)*pageSize);
        searchSourceBuilder.size(pageSize);

        SearchRequest searchRequest = new SearchRequest(config.getEsIndexVideoName());
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        SearchHits hits = searchResponse.getHits();
        Integer totalCount = (int)hits.getTotalHits().value;

        List<VideoInfo> list = new ArrayList<>();
        List<String> userIdList = new ArrayList<>();

        for(SearchHit searchHit : hits.getHits()){
            VideoInfo videoInfo = JsonUtils.convertJson2Obj(searchHit.getSourceAsString(),VideoInfo.class);
            if(searchHit.getHighlightFields().get("videoName") != null){
                videoInfo.setVideoName(searchHit.getHighlightFields().get("videoName").fragments()[0].toString());
            }
            list.add(videoInfo);
            userIdList.add(videoInfo.getUserId());
        }

        UserInfoQuery userInfoQuery = new UserInfoQuery();
        userInfoQuery.setUserIdList(userIdList);
        List<UserInfo> userInfoList = userInfoMapper.selectList(userInfoQuery);
        Map<String,UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(item -> item.getUserId(), Function.identity(),(data1, data2)->data2));

        list.forEach(item->{
            UserInfo userInfo = userInfoMap.get(item.getUserId());
            item.setNickName(userInfo == null ? "" : userInfo.getNickName());
        });
        SimplePage page = new SimplePage(pageNo,totalCount,pageSize);

        PaginationResultVO paginationResultVO = new PaginationResultVO(totalCount,page.getPageSize(),page.getPageNo(),list);

        return paginationResultVO;





    }



}
